/*!
 * @file       apm32f10x_usart.c
 *
 * @brief      This file provides all the USART firmware functions
 *
 * @details   
 *
 * @version    V1.0.0
 *   
 * @date       2019-8-7
 *
 */

#include "apm32f10x_usart.h"
#include "apm32f10x_rcm.h"

/*!
 * @brief     Reset usart peripheral registers to their default reset values
 *
 * @param     usart   Select the USART or the UART peripheral
 *
 * @retval    None
 *
 * @note 
 */
void USART_Reset(USART_T* usart)
{
   if(USART1 == usart)
   {
      RCM_EnableAPB2PeriphReset(RCM_APB2_PERIPH_USART1);
      RCM_DisableAPB2PeriphReset(RCM_APB2_PERIPH_USART1);
   }
   else if(USART2 == usart)
   {
      RCM_EnableAPB1PeriphReset(RCM_APB1_PERIPH_USART2);
      RCM_DisableAPB1PeriphReset(RCM_APB1_PERIPH_USART2);
   }
   else if(USART3 == usart)
   {
      RCM_EnableAPB1PeriphReset(RCM_APB1_PERIPH_USART3);
      RCM_DisableAPB1PeriphReset(RCM_APB1_PERIPH_USART3);
   }   
   else if(UART4 == usart)
   {
      RCM_EnableAPB1PeriphReset(RCM_APB1_PERIPH_UART4);
      RCM_DisableAPB1PeriphReset(RCM_APB1_PERIPH_UART4);
   }
   else if(UART5 == usart)
   {
      RCM_EnableAPB1PeriphReset(RCM_APB1_PERIPH_UART5);
      RCM_DisableAPB1PeriphReset(RCM_APB1_PERIPH_UART5);
   }   
}

/*!
 * @brief     Config the USART peripheral according to the specified parameters in the USART_InitStruct
 *
 * @param     uart         Select the USART or the UART peripheral
 *
 * @param     configStruct   pointer to a USART_ConfigStruct_T structure
 *
 * @retval    None
 *
 * @note 
 */
void USART_Config(USART_T* uart, USART_ConfigStruct_T *configStruct)
{
   uint32_t temp, fCLK, intDiv, fractionalDiv;
   
   /** WLS, PCEN, TXEN, RXEN */
   temp = uart->CTRL1;
   temp &= 0xE9F3;
   temp |= configStruct->mode | configStruct->parity | configStruct->wordLength;
   uart->CTRL1 = temp;

   /** STOP bits */
   temp = uart->CTRL2;
   temp &= 0xefff;
   temp |= configStruct->stopBits;
   uart->CTRL2 = temp;

   /** Hardware Flow Control */
   temp = uart->CTRL3;
   temp &= 0xFCFF;
   temp |= (uint32_t)configStruct->hardwareFlowCtrl;
   uart->CTRL3 = temp;

   if(uart == USART1)
   {
      RCM_GetPCLKFreq(NULL, &fCLK);
   }
   else
   {
      RCM_GetPCLKFreq(&fCLK, NULL);
   }
   
   intDiv = ((25 * fCLK) / (4 * (configStruct->baudRate))); 
   temp = (intDiv / 100) << 4;
   fractionalDiv = intDiv - (100 * (temp >> 4));
   temp |= ((((fractionalDiv * 16) + 50) / 100)) & ((uint8_t)0x0F);
   
   uart->BR = temp;
}

/*!
 * @brief     Fills each USART_InitStruct member with its default value
 *
 * @param     configStruct   pointer to a USART_ConfigStruct_T structure which will be initialized
 *
 * @retval    None
 *
 * @note 
 */
void USART_StructInit(USART_ConfigStruct_T* configStruct)
{
   configStruct->baudRate = 9600;
   configStruct->wordLength = USART_WORD_LEN_8B;
   configStruct->stopBits = USART_STOP_BIT_1;
   configStruct->parity = USART_PARITY_NONE ;
   configStruct->mode = USART_MODE_TX_RX;
   configStruct->hardwareFlowCtrl = USART_FLOW_CTRL_NONE;  
}

/*!
 * @brief     Enable/Disable CK PIN
 *
 * @param     uart   Select the USART or the UART peripheral
 *
 * @param     ckPin   Enable/Disable CK PIN
 *
 * @retval    None
 *
 * @note 
 */
void USART_ConfigCKPin(USART_T* uart, USART_CK_PIN_T ckPin)
{
   uart->CTRL2_B.CKPEN = ckPin;
}

/*!
 * @brief     Config clock polarity
 *
 * @param     uart      Select the USART or the UART peripheral
 *
 * @param     polarity   specifies the clock polarity
 *
 * @retval    None
 *
 * @note 
 */
void USART_CofigClkPolarity(USART_T* uart, USART_CLKPOL_T polarity)
{
   uart->CTRL2_B.CLKPOL = polarity;
}

/*!
 * @brief     Config clock phase
 *
 * @param     uart      Select the USART or the UART peripheral
 *
 * @param     phase      specifies the clock phase
 *
 * @retval    None
 *
 * @note 
 */
void USART_ConfigClkPhase(USART_T* uart, USART_CLKPHA_T phase)
{
   uart->CTRL2_B.CLKPHA = phase;
}

/*!
 * @brief     Config last bit clock
 *
 * @param     uart      Select the USART or the UART peripheral
 *
 * @param     lbc         Enable/Disable last bit clock
 *
 * @retval    None
 *
 * @note 
 */
void USART_ConfigLastBitClock(USART_T* uart, USART_LBC_T lbc)
{
   uart->CTRL2_B.LBCEN = lbc;
}


/*!
 * @brief     Enables the specified USART peripheral
 *
 * @param     usart   Select the USART or the UART peripheral
 *
 * @retval    None
 *
 * @note 
 */
void USART_Enable(USART_T* usart)
{
   usart->CTRL1_B.UEN = BIT_SET;
}

/*!
 * @brief     Disable the specified USART peripheral
 *
 * @param     usart   Select the USART or the UART peripheral
 *
 * @retval    None
 *
 * @note 
 */
void USART_Disable(USART_T* usart)
{
   usart->CTRL1_B.UEN = BIT_RESET;
}

/*!
 * @brief     Transmitter Enable
 *
 * @param     usart   Select the USART or the UART peripheral
 *
 * @retval    None
 *
 * @note 
 */
void USART_EnableTX(USART_T * usart)
{
   usart->CTRL1_B.TXEN = BIT_SET;
}

/*!
 * @brief     Transmitter Disable
 *
 * @param     usart   Select the USART or the UART peripheral
 *
 * @retval    None
 *
 * @note 
 */
void USART_DisableTX(USART_T * usart)
{
   usart->CTRL1_B.TXEN = BIT_RESET;
}

/*!
 * @brief     Receiver enable
 *
 * @param     usart   Select the USART or the UART peripheral
 *
 * @retval    None
 *
 * @note 
 */
void USART_EnableRX(USART_T * usart)
{
   usart->CTRL1_B.RXEN = BIT_SET;
}

/*!
 * @brief     Receiver disable
 *
 * @param     usart   Select the USART or the UART peripheral
 *
 * @retval    None
 *
 * @note 
 */
void USART_DisableRX(USART_T * usart)
{
   usart->CTRL1_B.RXEN = BIT_RESET;
}

/*!
 * @brief     Disables the specified USART interrupts
 *
 * @param     usart      Select the USART or the UART peripheral
 *
 * @param     interrupt   specifies the USART interrupt sources to be enabled or disabled
 *
 * @retval    None
 *
 * @note 
 */
void USART_DisableInterrupt(USART_T *usart, USART_INT_T interrupt)
{
   uint32_t temp;

   temp = (uint32_t)~(interrupt & 0xffff);

   if(interrupt & 0X10000)
   {
      usart->CTRL1 &= temp;
   }

   if(interrupt & 0X20000)
   {
      usart->CTRL2 &= temp;
   }

   if(interrupt & 0X40000)
   {
      usart->CTRL3 &= temp;
   }   
}

/*!
 * @brief     Enable the specified USART interrupts
 *
 * @param     usart      Select the USART or the UART peripheral
 *
 * @param     interrupt   specifies the USART interrupt sources to be enabled or disabled
 *
 * @retval    None
 *
 * @note 
 */
void USART_EnableInterrupt(USART_T *usart, USART_INT_T interrupt)
{
   uint32_t temp;

   temp = (uint32_t)(interrupt & 0xffff);

   if(interrupt & 0X10000)
   {
      usart->CTRL1 |= temp;
   }

   if(interrupt & 0X20000)
   {
      usart->CTRL2 |= temp;
   }

   if(interrupt & 0X40000)
   {
      usart->CTRL3 |= temp;
   }   
} 

/*!
 * @brief     Enables the USART DMA interface
 *
 * @param     usart   Select the USART or the UART peripheral
 *
 * @param     mdaReq   specifies the DMA request
 *
 * @retval    None
 *
 * @note 
 */
void USART_EnableDMA(USART_T *usart, USART_DMA_T mdaReq)
{
   usart->CTRL3 |= mdaReq;
}

/*!
 * @brief     Disable the USART DMA interface
 *
 * @param     usart   Select the USART or the UART peripheral
 *
 * @param     mdaReq   specifies the DMA request
 *
 * @retval    None
 *
 * @note 
 */
void USART_DisableDMA(USART_T *usart, USART_DMA_T mdaReq)
{
   usart->CTRL3 &= (uint32_t)~mdaReq;
}

/*!
 * @brief     Sets the address of the USART node
 *
 * @param     usart   Select the USART or the UART peripheral
 *
 * @param     address   Indicates the address of the USART node
 *
 * @retval    None
 *
 * @note 
 */
void USART_SetAddress(USART_T* usart, uint8_t address)
{
   usart->CTRL2_B.ADDR = address;
}

/*!
 * @brief     address   Indicates the address of the USART node
 *
 * @param     usart         Select the USART or the UART peripheral
 *
 * @param     wakeupMethod   specifies the USART wakeup method
 *
 * @retval    None
 *
 * @note 
 */
void USART_ConfigWakeUp(USART_T* usart, USART_WAKEUP_T wakeupMethod)
{
   usart->CTRL1_B.WUPM = wakeupMethod;
}

/*!
 * @brief     Set USART Receiver in mute mode
 *
 * @param     usart   Select the USART or the UART peripheral
 *
 * @retval    None
 *
 * @note 
 */
void USART_EnableMuteMode(USART_T* usart)
{
   usart->CTRL1_B.RXWUP = BIT_SET;
}

/*!
 * @brief     Set USART Receiver in active mode
 *
 * @param     usart   Select the USART or the UART peripheral
 *
 * @retval    None
 *
 * @note 
 */
void USART_DisableMuteMode(USART_T* usart)
{
   usart->CTRL1_B.RXWUP = BIT_RESET;
}

/*!
 * @brief     Sets the USART LIN Break detection length
 *
 * @param     usart   Select the USART or the UART peripheral
 *
 * @param     LBDL   specifies the LIN break detection length
 *
 * @retval    None
 *
 * @note 
 */
void USART_ConfigLINBreakDetectLength(USART_T* usart, USART_LBDL_T LBDL)
{
   usart->CTRL2_B.LBDL = LBDL;
}

/*!
 * @brief     Enables the USART LIN MODE
 *
 * @param     usart   Select the USART or the UART peripheral
 *
 * @retval    None
 *
 * @note 
 */
void USART_EnableLIN(USART_T* usart)
{
   usart->CTRL2_B.LINEN = BIT_SET;
}

/*!
 * @brief     Disable the USART LIN MODE
 *
 * @param     usart   Select the USART or the UART peripheral
 *
 * @retval    None
 *
 * @note 
 */
void USART_DisableLIN(USART_T* usart)
{
   usart->CTRL2_B.LINEN = BIT_RESET;
}

/*!
 * @brief     Transmits single data
 *
 * @param     usart   Select the USART or the UART peripheral
 *
 * @param     data   the data to transmit
 *
 * @retval    None
 *
 * @note 
 */
void USART_TxData(USART_T* usart, uint16_t data)
{
   usart->DATA_B.DATA = data;
}

/*!
 * @brief     Returns the most recent received data
 *
 * @param     usart   Select the USART or the UART peripheral
 *
 * @retval    None
 *
 * @note 
 */
uint16_t USART_RxData(USART_T* usart)
{
  return (uint16_t)(usart->DATA_B.DATA);
}

/*!
 * @brief     Transmits break characters
 *
 * @param     usart   Select the USART or the UART peripheral
 *
 * @retval    None
 *
 * @note 
 */
void USART_TxBreak(USART_T* usart)
{
   usart->CTRL1_B.TXBK = BIT_SET;
}

/*!
 * @brief     Sets the specified USART guard time
 *
 * @param     usart      Select the USART or the UART peripheral
 *
 * @param     guardTime   specifies the guard time
 *
 * @retval    None
 *
 * @note      The guard time bits are not available for UART4 and UART5
 */
void USART_ConfigGuardTime(USART_T* usart, uint8_t guardTime)
{    
   usart->GTDIV_B.GT = guardTime;
}

/*!
 * @brief     Sets the system clock divider number
 *
 * @param     usart   Select the USART or the UART peripheral
 *
 * @param     div      specifies the divider number
 *
 * @retval    None
 *
 * @note 
 */
void USART_ConfigDivNumber(USART_T* usart, uint8_t div)
{ 
   usart->GTDIV_B.DIV = div;
}

/*!
 * @brief     Enables the USART Smart Card mode
 *
 * @param     usart   Select the USART or the UART peripheral
 *
 * @retval    None
 *
 * @note      The Smart Card mode is not available for UART4 and UART5
 */
void USART_EnableSmartCard(USART_T* usart)
{
   usart->CTRL3_B.SCEN = BIT_SET;
}

/*!
 * @brief     Disable the USART Smart Card mode
 *
 * @param     usart   Select the USART or the UART peripheral
 *
 * @retval    None
 *
 * @note 
 */
void USART_DisableSmartCard(USART_T* usart)
{
   usart->CTRL3_B.SCEN = BIT_RESET;
}

/*!
 * @brief     Enables NACK transmission
 *
 * @param     usart   Select the USART or the UART peripheral
 *
 * @retval    None
 *
 * @note      The Smart Card mode is not available for UART4 and UART5
 */
void USART_EnableSmartCardNACK(USART_T* usart)
{
   usart->CTRL3_B.NACKEN = BIT_SET;
}

/*!
 * @brief     Disable NACK transmission
 *
 * @param     usart   Select the USART or the UART peripheral
 *
 * @retval    None
 *
 * @note 
 */
void USART_DisableSmartCardNACK(USART_T* usart)
{
   usart->CTRL3_B.NACKEN = BIT_RESET;
}


/*!
 * @brief     Enables USART Half Duplex communication
 *
 * @param     usart   Select the USART or the UART peripheral
 *
 * @retval    None
 *
 * @note 
 */
void USART_EnableHalfDuplex(USART_T* usart)
{
   usart->CTRL3_B.HDEN = BIT_SET;
}

/*!
 * @brief     Disable USART Half Duplex communication
 *
 * @param     usart   Select the USART or the UART peripheral
 *
 * @retval    None
 *
 * @note 
 */
void USART_DisableHalfDuplex(USART_T* usart)
{
   usart->CTRL3_B.HDEN = BIT_RESET;
}

/*!
 * @brief     Configures the USART's IrDA interface
 *
 * @param     usart   Select the USART or the UART peripheral 
 *
 * @param     IrDAMode   specifies the IrDA mode
 *
 * @retval    None
 *
 * @note 
 */
void USART_ConfigIrDA(USART_T* usart, USART_IRDALP_T IrDAMode)
{
   usart->CTRL3_B.IRDALP = IrDAMode;
}

/*!
 * @brief     Enables the USART's IrDA interface
 *
 * @param     usart   Select the USART or the UART peripheral
 *
 * @retval    None
 *
 * @note 
 */
void USART_EnableIrDA(USART_T* usart)
{
   usart->CTRL3_B.IRDAEN = BIT_SET;
}

/*!
 * @brief     Disable the USART's IrDA interface
 *
 * @param     usart   Select the USART or the UART peripheral
 *
 * @retval    None
 *
 * @note 
 */
void USART_DisableIrDA(USART_T* usart)
{
   usart->CTRL3_B.IRDAEN = BIT_RESET;
}

/*!
 * @brief     Read the specified USART flag
 *
 * @param     usart   Select the USART or the UART peripheral  
 *
 * @param     flag   specifies the flag to check
 *
 * @retval    The new state of flag (SET or RESET)
 *
 * @note 
 */
uint8_t USART_ReadFlag(USART_T* usart, USART_FLAG_T flag)
{
   return (usart->STS & flag) ? SET : RESET;
}

/*!
 * @brief     Clears the USARTx's pending flags
 *
 * @param     usart   Select the USART or the UART peripheral 
 *
 * @param     flag   specifies the flag to clear.
 *
 * @retval    None
 *
 * @note 
 */
void USART_ClearFlag(USART_T* usart, USART_FLAG_T flag)
{   
   usart->STS &= (uint32_t)~flag;
}

/*!
 * @brief     Read the specified USART interrupt flag
 *
 * @param     usart   Select the USART or the UART peripheral  
 *
 * @param     intSrc   specifies the USART interrupt source to check
 *
 * @retval    The new state of intSrc (SET or RESET)
 *
 * @note 
 */
uint8_t USART_ReadIntFlag(USART_T* usart, USART_INT_T intSrc)
{
   uint32_t itFlag, srFlag;
   
   if(intSrc & 0x10000)
   {
      itFlag = usart->CTRL1 & intSrc & 0xffff;
   }
   else if(intSrc & 0x20000)
   {
      itFlag = usart->CTRL2 & intSrc & 0xffff;
   }
   else
   {
      itFlag = usart->CTRL3 & intSrc & 0xffff;
   }

   srFlag = intSrc >> 24;
   srFlag = (uint32_t)(1 << srFlag);
   srFlag = usart->STS & srFlag;
   
   if(srFlag && itFlag)
   {
      return SET;
   }

   return RESET;
}

/*!
 * @brief     Clears the USART interrupt pending bits
 *
 * @param     usart   Select the USART or the UART peripheral 
 *
 * @param     intSrc   specifies the interrupt pending bit to clear
 *
 * @retval    None
 *
 * @note 
 */
void USART_ClearIntFlag(USART_T* usart, USART_INT_T intSrc)
{
   uint32_t srFlag;

   srFlag = intSrc >> 24;
   srFlag = (uint32_t)(1 << srFlag);
   
     usart->STS &= (uint32_t)~srFlag;
}

